#ifndef PERLIN_H
#define PERLIN_H

#include "util.h"

class Perlin {
public:
    Perlin() {
        randfloat = new double[point_count];
        for (int i = 0; i < point_count; i++) {
            randfloat[i] = random_double();
        }

        perm_x = perlin_generate_perm();
        perm_y = perlin_generate_perm();
        perm_z = perlin_generate_perm();
    }

    ~Perlin() {
        delete[] randfloat;
        delete[] perm_x;
        delete[] perm_y;
        delete[] perm_z;
    }

    double noise(const Point3& p) const;

private:
    static const int point_count = 256;
    double* randfloat;
    int* perm_x;
    int* perm_y;
    int* perm_z;

    static double trilinear_interp(double c[2][2][2], double u, double v, double w) {
        auto accum = 0.0;
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                for (int k = 0; k < 2; k++)
                    accum += (i * u + (1 - i) * (1 - u))
                    * (j * v + (1 - j) * (1 - v))
                    * (k * w + (1 - k) * (1 - w))
                    * c[i][j][k];

        return accum;
    }

    static int* perlin_generate_perm() {
        auto p = new int[point_count];

        for (int i = 0; i < point_count; i++)
            p[i] = i;

        permute(p, point_count);

        return p;
    }

    static void permute(int* p, int n) {
        for (int i = n - 1; i > 0; i--) {
            int target = random_int(0, i);
            int tmp = p[i];
            p[i] = p[target];
            p[target] = tmp;
        }
    }
};

#endif